#include "Altavoz.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

using std::cout;
using std::cerr;
using std::endl;

Altavoz::Altavoz() : DispositivoAudio() {
	int ret;
	unsigned int val;
	Decodificador = new Deco_Audio();
	Decodificador->register_codecs();
	// Abro el dispositivo
	ret = snd_pcm_open(&this->handle, "hw", SND_PCM_STREAM_PLAYBACK, 0);
	if (ret < 0) {
		cerr << "unable to open pcm device: " << snd_strerror(ret) << endl;
		exit(1);
	}
	// Allocate a hardware parameters object
	snd_pcm_hw_params_alloca(&this->params);
	// Valores por defecto
	snd_pcm_hw_params_any(this->handle, this->params);
	// modo interleaved
	snd_pcm_hw_params_set_access(this->handle, this->params,
			SND_PCM_ACCESS_RW_INTERLEAVED);
	// Formato signed 16-bit little-endian
	snd_pcm_hw_params_set_format(this->handle, this->params,
			SND_PCM_FORMAT_S16_LE);
	// Seteo cantidad de canales
	snd_pcm_hw_params_set_channels(this->handle, this->params, this->canales);
	val = 44100; // bits/segundo sampling rate (calidad CD)
	snd_pcm_hw_params_set_rate_near(this->handle, this->params, &val,
			&this->dir);
	snd_pcm_hw_params_set_period_size_near(this->handle, this->params,
			&this->frames, &this->dir);
	// Cargo los parametros al driver
	ret = snd_pcm_hw_params(this->handle, this->params);
	if (ret < 0) {
		cerr << "unable to set hw parameters: " << snd_strerror(ret) << endl;
		exit(1);
	}
	// Tamanio del periodo
	snd_pcm_hw_params_get_period_size(this->params, &this->frames, &this->dir);

	Decodificador->busca_codec();

	Decodificador->ini_contexto();

	Decodificador->abre_codec();
}

void Altavoz::ejecutar() {
	FILE *arch;
	arch = fopen("salida.mp2", "r");
	int size_FrameDeodificado = 0;
	uint8_t *inbuf = NULL;
	int ret;
	signed short* aux; // Puntero auxiliar
	int faltan; // Frames que faltan para completar un periodo
	int len = 0;
	//uint8_t *FrameDecodificado = (uint8_t*)av_malloc((AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2);
	uint8_t FrameDecodificado[AVCODEC_MAX_AUDIO_FRAME_SIZE + (2*2*1152)];

	AVPacket packet;
	//inbuf_ptr = inbuf;
	bool salir = false;
	av_init_packet(&packet);
	while (1) {
		//read(STDIN_FILENO, buf, sizeof(short) * this->buf_size);
		inbuf = (uint8_t*) av_malloc(4096 + FF_INPUT_BUFFER_PADDING_SIZE);
		int size = fread(inbuf, 1, 4096, arch);
		if(size == 0) {
			cerr << "No leyo nada" << endl;
			salir = true;
			break;
		}

		//inbuf_ptr = inbuf;
		cout << "Entra 1" << endl;
		packet.size = size;
		cout << "Entra 1" << endl;
		packet.data = inbuf;
		cout << "Entra 1" << endl;
		while(size > 0) {
			size_FrameDeodificado = sizeof(FrameDecodificado);

			//len = Decodificador->decodificar(&FrameDecodificado, &size_FrameDeodificado, &inbuf, size);
			len = avcodec_decode_audio3(Decodificador->getDecoder_Ctx(), (int16_t*)FrameDecodificado, &size_FrameDeodificado, &packet);
			if(len < 0) {
				cerr << "Error decodificando" << endl;
				break;
			}
			cout << size_FrameDeodificado << endl;
			if(size_FrameDeodificado > 0) {
				cout << "Frame decodificado" << endl;
				aux = (int16_t*)FrameDecodificado;
				//faltan = this->frames;
				cout << "Se decodifico un frame" << endl;
				ret = snd_pcm_writei(this->handle, aux, faltan);
				if (ret == -EAGAIN)
					continue;
				if (ret < 0) {
					if (this->recuperacion(ret) < 0) {
						cerr << "speaker: error al escribir: " << snd_strerror(ret)
								<< endl;
						exit(EXIT_FAILURE);
					}
					break; // Descarto el periodo
				}
				if (ret == 0) {
					cerr << "fin de archivo" << endl;
					break;
				}
				if (ret == -EPIPE) {
					// EPIPE = underrun
					cerr << "altavoz: underrun" << endl;
					snd_pcm_prepare(this->handle);
				}
				aux += ret * this->canales;
				faltan -= ret;
			}
			packet.size -= len;
			packet.data += len;
		}
		/*aux = this->buf;
		faltan = this->frames;
		while (faltan > 0) {
			ret = snd_pcm_writei(this->handle, aux, faltan);
			if (ret == -EAGAIN)
				continue;
			if (ret < 0) {
				if (this->recuperacion(ret) < 0) {
					cerr << "speaker: error al escribir: " << snd_strerror(ret)
							<< endl;
					exit(EXIT_FAILURE);
				}
				break; // Descarto el periodo
			}
			if (ret == 0) {
				cerr << "fin de archivo" << endl;
				break;
			}
			if (ret == -EPIPE) {
				// EPIPE = underrun
				cerr << "altavoz: underrun" << endl;
				snd_pcm_prepare(this->handle);
			}
			aux += ret * this->canales;
			faltan -= ret;
		}*/
	}
	if(!salir) {
		snd_pcm_drain(this->handle);
		snd_pcm_close(this->handle);
	}
	fclose(arch);
}

Altavoz::~Altavoz() {
}
